import sys
from typing import Callable, List, Tuple, Union, Iterable

from PyQt5 import QtGui, QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QCursor
from PyQt5.QtWidgets import QWidget, QApplication, QHeaderView, QTableWidgetItem, QHBoxLayout, QPushButton, QGridLayout, \
    QFrame

from dao.noteDao import NoteDao
from domain.util.pageDomain import PageDomain
from domain.util.tableColumn import TableColumn
from model.components.pageWidget import PageWidget
from util.aop.ormProxy import OrmProxy
from view.components.ui_tableWidget import Ui_TableWidget


class TableWidget(QWidget, Ui_TableWidget):
    _DEFAULT_COL = []

    @staticmethod
    def _defaultData(_):
        return 0, []

    def __init__(self,
                 columns: List[TableColumn] = None,
                 data: Callable[[PageDomain], Union[List, Tuple]] = None,
                 parent=None):
        """
        :param columns: 列参数
            title: str                          # 表头
            index: str                          # 索引名
            slot: Callable[[object], QWidget]   # 需要替换的函数，参数是对应的列值，返回值需要一个QWidget对象
            textFilter: Callable[[str], str]    # 需要替换的函数，参数是对应的列值，返回值需要一个str对象
        :param data: 用于获取数据的函数
            参数为封装的查询对象，返回值是元组或列表，并且第一个值是数据总数，第二个是当前页的结果
        :param parent:
        """
        super().__init__(parent)
        if columns is None:
            columns = self._DEFAULT_COL
        if data is None:
            data = self._defaultData
        self.setupUi(self)

        self._pagination = PageWidget()
        self.pageLayout.addWidget(self._pagination, 0)

        self._cols = columns
        # print(columns[0].title)
        self._data = data
        self._param = self.getPageDomain(self._pagination.getPageInfo())
        self._rows = []

        self.initTable()
        self._pagination.changed.connect(self.onPageChanged)

    def setPageSizeIndex(self, index):
        """
        设置pageSize索引
            0 -> 5
            1 -> 10
            2 -> 20
            3 -> 50
        """
        self._pagination.setPageSizeIndex(index)

    @staticmethod
    def getPageDomain(pageInfo: dict) -> PageDomain:
        """
        封装分页器的数据
        :param pageInfo: 分页器传递的字典
        :return: PageDomain对象
        """
        return PageDomain(pageInfo.get('pageNum'), pageInfo.get('pageSize'))

    def fetchData(self):
        """
        调用_data函数获取参数
        :return:
        """
        total, self._rows = self._data(self._param)
        # 如果_data函数有误，获取的数据超出了pageSize，舍弃超出的部分
        if len(self._rows) > self._param.pageSize:
            print('超出了单页容量，请检查data函数。当前获取的数据：')
            print(self._rows)
            self._rows = self._rows[:self._param.pageSize]
        self._pagination.setTotal(total)

    def initTable(self):
        """
        初始化表格
        """
        self.table.setColumnCount(len(self._cols))
        self.table.setHorizontalHeaderLabels([col.title for col in self._cols])
        self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        for i, col in enumerate(self._cols):
            if col.width is not None:
                if col.width > 0:
                    self.table.horizontalHeader().setSectionResizeMode(i, QHeaderView.Fixed)
                    self.table.setColumnWidth(i, col.width)
                elif col.width == 0:
                    self.table.horizontalHeader().setSectionResizeMode(i, QHeaderView.Interactive)
        # self.refresh()

    def drawTable(self):
        """
        绘制表格
        """
        # print(self._rows)
        self.table.setRowCount(len(self._rows))
        for i, row in enumerate(self._rows):
            for j, col in enumerate(self._cols):
                slot = col.slot
                textFilter = col.textFilter
                text = row.get(col.index)
                if slot:
                    container = QWidget()
                    wrapper = QGridLayout()

                    content = QWidget()
                    layout = QHBoxLayout()
                    for k, widget in enumerate(slot(text, row)):
                        if k > 0:
                            layout.addWidget(divideLine())
                        layout.addWidget(widget)
                    layout.setContentsMargins(0, 8, 0, 8)
                    content.setLayout(layout)

                    wrapper.addWidget(content, 0, 0, 0, 0, Qt.AlignHCenter)
                    wrapper.setContentsMargins(0, 0, 0, 0)
                    container.setLayout(wrapper)
                    # content.setStyleSheet("border:1px solid black;")
                    # container.setStyleSheet(".QWidget{border:1px solid red}")
                    self.table.setCellWidget(i, j, container)
                else:
                    if textFilter is not None:
                        text = textFilter(text, row)
                    if text is None:
                        text = ''
                    item = QTableWidgetItem(str(text))
                    item.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
                    self.table.setItem(i, j, item)

    def refresh(self):
        """
        获取数据，更新表格
        :return:
        """
        self.fetchData()
        self.drawTable()
        return self

    def onPageChanged(self, param):
        """
        内部分页器发送信号的回调函数
        :param param: 分页器传递的参数
        """
        self._param = self.getPageDomain(param)
        self.refresh()


def decorateButton(button, color='#7aabd3') -> QPushButton:
    """
    修改按钮样式
    :param button: 按钮
    :param color: 按钮名字 用于修改颜色
    """
    font = QtGui.QFont()
    font.setFamily("微软雅黑")
    font.setPointSize(0)
    font.setBold(True)
    font.setWeight(75)
    button.setFont(font)
    button.setCursor(QCursor(Qt.PointingHandCursor))
    button.setStyleSheet(button.styleSheet() + "font-size:16px;background:transparent;color:{}".format(color))
    sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
    sizePolicy.setHorizontalStretch(0)
    sizePolicy.setVerticalStretch(0)
    sizePolicy.setHeightForWidth(button.sizePolicy().hasHeightForWidth())
    button.setSizePolicy(sizePolicy)
    return button


def divideLine() -> QWidget:
    line = QFrame()
    line.setFrameStyle(QFrame.VLine)
    line.setFrameShadow(QFrame.Raised)
    line.setStyleSheet('background-color: rgb(255, 255, 255);')
    return line


if __name__ == '__main__':
    app = QApplication(sys.argv)  # 创建GUI应用程序


    def actionButtons(text, row) -> Iterable[QWidget]:
        """
        自定义列内容，该函数为展示了绘制两个按钮的样例
        :param text: 列值
        :param row: 行值
        :return: 返回一个QWidget列表
        """
        return decorateButton(QPushButton('编辑')), decorateButton(QPushButton('删除'), 'red')


    def typeFilter(text, row):
        """
        自定义文本修饰过滤器
        :param text: 列值
        :param row: 行值
        :return: 返回修饰后的文本
        """
        # print(text, row)
        return text


    # 列参数样例
    COLUMNS = [
        TableColumn('日期', 'noteDate', textFilter=lambda x, _: str(x), width=150),
        TableColumn('开始', 'startTime', textFilter=lambda x, _: str(x), width=100),
        TableColumn('结束', 'endTime', textFilter=lambda x, _: str(x), width=100),
        TableColumn('类型', 'noteType', textFilter=lambda x, _: str(x), width=150),
        TableColumn('内容', 'noteContent', textFilter=lambda x, _: x if len(x) <= 36 else x[:33] + '...'),
        TableColumn('操作', 'action', actionButtons, width=0),
    ]


    # 获取数据的函数的样例
    def listTypes(param: PageDomain):
        OrmProxy.startPage(param.pageNum, param.pageSize, 'note_id desc')
        result = NoteDao().list4Table()
        return OrmProxy.getTotal(result), result


    table = TableWidget(COLUMNS, listTypes).refresh()
    # table.table.setHorizontalHeaderLabels([col.title for col in COLUMNS])
    table.show()
    app.exec_()
